# 日志系统
# write by hashaki
# first edit on 20181023 ,last change on 2019/06/08
# log的文件路径只能设置一次，所以回测和交易的日志都用txt，txt的write要传入str，writelines要传可迭代对象
# txt换行f.write('\r\n')
# 标准化的数据保存
import logging
import os
import csv
import shelve
from datetime import datetime
import json
import subprocess

import requests

try:
    from git import Repo
    has_git=True
except ModuleNotFoundError:
    has_git=False

base_path='{}/logs'.format(os.getcwd())
data_path='{}/data'.format(os.getcwd())
debug_path='/{}/debug'.format(base_path)
backtest_path='/{}/backtest'.format(base_path)
trade_path='/{}/trade'.format(base_path)
class DebugLog:
    def __init__(self,name):
        #配置输出日志格式
        if os.path.exists(debug_path):
            pass
        else:
            if os.path.exists(base_path):
                pass
            else:
                os.mkdir(base_path)
                os.mkdir(debug_path)

        self.log_path='{}/{}.log'.format(debug_path,name)
        DATE_FORMAT = '%Y/%m/%d  %H:%M:%S %a '
        self.log_format="%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s %(lineno)d"
        logging.basicConfig(level=logging.DEBUG,format=self.log_format,datefmt =DATE_FORMAT,filename=self.log_path)

    def errorLog(self,content):
        '''系统错误日志'''
        logging.error(content)

    def infoLog(self,content):
        '''信息日志'''
        logging.info(content)

    def criticalLog(self,content):
        '''灾难日志'''
        logging.critical(content)

    def warningLog(self,content):
        '''系统警告日志'''
        logging.warning(content)
    
    def debugLog(self,content):
        '''调试日志'''
        logging.debug(content)

    def printLog(self,content):
        '''终端输出日志，看上去就知道很耗资源，别乱用'''
        print(content)

class BacktestResultLog:
    def __init__(self,name):
        '''回测日志'''
        if os.path.exists(backtest_path):
            pass
        else:
            if os.path.exists(base_path):
                os.mkdir(backtest_path)
            else:
                os.mkdir(base_path)
                os.mkdir(backtest_path)

        self.log_path='{}/{}.log'.format(backtest_path,name)
        
    def accountLog(self,content):
        '''账户日志'''
        with open(self.log_path,'a') as f:
            f.write(content+'\r\n')
    
    def backtestOrderLog(self,content):
        '''回测下单日志'''
        with open(self.log_path,'a') as f:
            f.write(content+'\r\n')
    
    def backtestTradeLog(self,content):
        '''回测交易日志'''
        with open(self.log_path,'a') as f:
            f.write(content+'\r\n')
    
    def backtestResultLog(self,content):
        '''回测结果日志'''
        with open(self.log_path,'a') as f:
            f.write(content+'\r\n')
    
class TradeResultLog:
    def __init__(self,name):
        '''交易日志'''
        if os.path.exists(trade_path):
            pass
        else:
            if os.path.exists(base_path):
                os.mkdir(trade_path)
            else:
                os.mkdir(base_path)
                os.mkdir(trade_path)

        self.log_path='{}/{}.log'.format(trade_path,name)

    def accountLog(self,content):
        with open(self.log_path,'a') as f:
            f.write(content+'\r\n')
    
    def TradeOrderLog(self,content):
        '''交易下单日志'''
        with open(self.log_path,'a') as f:
            f.write(content+'\r\n')
    
    def TradeTradeLog(self,content):
        '''成交日志'''
        with open(self.log_path,'a') as f:
            f.write(content+'\r\n')

class SmallSaveCSV:
    def __init__(self):
        '''标准化保存成csv数据'''
        self.path=None                     # 保存的路径
        self.file_name=None                # 保存的文件名字
        self.csv_header=None               # csv列标题
        self.row=None                      # csv每一行的数据
    
    # -----------------------------------------------------------------
    # 数据进来部分
    def setting(self,path,file_name,csv_header,data):
        '''path为data文件夹下的文件夹名字，file_name为path下的最终文件名,示例：
           csv_header=['strategy_name','side','symbol','price','volume','time']
           data=[(1,2,3),(4,5,6),(7,8,9),('d','f','g')]'''
        self.path=path
        self.file_name=file_name
        self.csv_header=csv_header
        self.row=data
    
    # ------------------------------------------------------------------
    # 路径检查部分
    def checkDataPath(self):
        '''检测最外层的data文件夹有没有存在'''
        if os.path.exists(data_path):
            return
        else:
            os.mkdir(data_path)
    
    def checkSpecialPath(self):
        '''检测data文件夹下面有没有对应的子文件夹'''
        temp=data_path+'/'+self.path
        if os.path.exists(temp):
            return
        else:
            os.mkdir(temp)
    
    def checkFile(self):
        '''检测该文件是否存在，不存在再创建，存在则写进'''
        temp=data_path+'/'+self.path+'/'+self.file_name
        if os.path.exists(temp):
            # 文件已存在，直接写进
            f=open(temp,'a')
            f_csv=csv.writer(f)
            f_csv.writerows(self.row)
            f.close()
        else:
            # 文件夹不存在的情况
            f=open(temp,'w')
            f_csv=csv.writer(f)
            f_csv.writerow(self.csv_header)
            f_csv.writerows(self.row)
            f.close()
            
    # ------------------------------------------------------------------
    # 保存部分
    def save(self):
        '''开始保存，要先调用setting函数'''
        if not self.path:
            raise KeyError("需要调用setting设置下路径")
        self.checkDataPath()
        self.checkSpecialPath()
        self.checkFile()
    

class SaveShelve:
    def __init__(self):
        self.path_name=None                             # shelve保存的文件夹名称
        self.file_name=None                             # 持久化数据库名称
        self.key_name=None                              # 数据库下键值的名称
        self.data=None                                  # 需要保存的数据

    # ------------------------------------------------------------------
    # 设置部分
    def setting(self,path_name,file_name,key_name,data):
        '''path_name为文件夹名称，file_name为持久化数据库名称，data为需要持久化的数据,key_name为
        数据库下键值的名称，data为一个字典结构'''
        self.path_name=path_name
        self.file_name=file_name
        self.key_name=key_name
        self.data=data
    
    # -----------------------------------------------------------------
    # 检查路径部分
    def checkDataPath(self):
        '''检测最外层的data文件夹有没有存在'''
        if os.path.exists(data_path):
            return
        else:
            os.mkdir(data_path)
    
    def checkSpecialPath(self):
        '''检测data文件夹下面有没有对应的子文件夹'''
        temp=data_path+'/'+self.path_name
        if os.path.exists(temp):
            return
        else:
            os.mkdir(temp)
    
    def checkFile(self):
        '''检测该文件是否存在，不存在再创建，存在则写进'''
        temp=data_path+'/'+self.path_name+'/'+self.file_name
        # 若没有shelve数据库，open会自动创建
        base=shelve.open(temp,writeback=True)
        if len(base)==0:
            temp_list=[]
            base[self.key_name]=temp_list
            base[self.key_name].append(self.data)
            base.close()
        else:
            base[self.key_name].append(self.data)
            base.close()
    
    # -------------------------------------------------------------------
    # 最后保存
    def save(self):
        '''运行保存，要先调用setting函数'''
        if not self.path_name:
            raise KeyError("需要调用setting设置下路径")
        self.checkDataPath()
        self.checkSpecialPath()
        self.checkFile()

class SaveShelveOnce:
    def __init__(self):
        '''这个持久化不会加写，只会保存一条数据，新的数据来就覆盖'''
        self.path_name=None                             # shelve保存的文件夹名称
        self.file_name=None                             # 持久化数据库名称
        self.key_name=None                              # 数据库下键值的名称
        self.data=None                                  # 需要保存的数据
        
    # ------------------------------------------------------------------
    # 设置部分
    def setting(self,path_name,file_name,key_name,data):
        '''path_name为文件夹名称，file_name为持久化数据库名称，data为需要持久化的数据,key_name为
        数据库下键值的名称，data为一个字典结构'''
        self.path_name=path_name
        self.file_name=file_name
        self.key_name=key_name
        self.data=data

    # -----------------------------------------------------------------
    # 检查路径部分
    def checkDataPath(self):
        '''检测最外层的data文件夹有没有存在'''
        if os.path.exists(data_path):
            return
        else:
            os.mkdir(data_path)
    
    def checkSpecialPath(self):
        '''检测data文件夹下面有没有对应的子文件夹'''
        temp=data_path+'/'+self.path_name
        if os.path.exists(temp):
            return
        else:
            os.mkdir(temp)
    
    def checkFile(self):
        '''检测该文件是否存在，不存在再创建，存在则写进'''
        temp=data_path+'/'+self.path_name+'/'+self.file_name
        # 若没有shelve数据库，open会自动创建
        base=shelve.open(temp,writeback=True)
        base[self.key_name]=self.data
        base.close()
    
    # -------------------------------------------------------------------
    # 最后保存
    def save(self):
        '''运行保存，要先调用setting函数'''
        if not self.path_name:
            raise KeyError("需要调用setting设置下路径")
        self.checkDataPath()
        self.checkSpecialPath()
        self.checkFile()

class ReadShelveData:
    def __init__(self):
        '''读取shelve数据'''
        self.path_name=None                             # shelve保存的文件夹名称
        self.file_name=None                             # 持久化数据库名称
        self.key_name=None                              # 数据库下键值的名称
        self.data=None                                  # 需要保存的数据

    # ---------------------------------------------------------------------
    # 设定部分
    def setting(self,path_name,file_name,key_name,data):
        '''path_name为文件夹名称，file_name为持久化数据库名称，data为需要持久化的数据,key_name为
        数据库下键值的名称，data为一个字典结构'''
        self.path_name=path_name
        self.file_name=file_name
        self.key_name=key_name
        self.data=data
    
    # ---------------------------------------------------------------------
    # 检测判断部分
    def checkFile(self):
        '''检测文件是否存在'''
        file_path=data_path+'/'+self.path_name+'/'+self.file_name
        if not os.path.exists(file_path):
            raise Exception("{}/{}持久化文件不存在".format(self.path_name,self.file_name))
        
    # ----------------------------------------------------------------------
    def read(self):
        '''开始读取文件内容'''
        self.checkFile()

class Ding:
    def __init__(self):
        '''发送消息到钉钉群'''
        self.ding_host=''                # 钉钉群接收地址
        self.content=''                  # 要发送的内容
        self.header={"Content-Type":"application/json ;charset=utf-8"}
        self.atAll=False                 # 是否要@所有人

    # ----------------------------------------------------------------------
    def setting(self,webhost):
        '''先设置钉钉地址'''
        self.ding_host=webhost
    
    # ----------------------------------------------------------------------
    def send(self,string):
        '''string是要发送的内容'''
        self.content=string
        if self.atAll:
            msg={"msgtype":"text","text":{"content":self.content},"at":{"isAtAll":1}}
        else:
            msg={"msgtype":"text","text":{"content":self.content}}

        msg_str=json.dumps(msg)
        res=requests.post(self.ding_host,data=msg_str,headers=self.header)
    
    def send_pic(self,pic_url,title):
        '''发送图片'''
        origin_data={"msgtype":"markdown","markdown":{"title":title,"text":"![]({})\n".format(pic_url)}}
        print(origin_data)
        msg=json.dumps(origin_data)
        requests.post(self.ding_host,data=msg,headers=self.header)

class SaveOnGit:
    def __init__(self):
        self.git_host=''              # git的地址
        self.file_path=''             # 要上传的文件路径
        self.account=''               # git账号
        self.screte=''                # git账号密码
        self.branch=''                # 分支名称
        self.git=None                 # gitPython的类

        # 以下是git的命令变量
        self.add=''                   # 本地代码修改后的更新命令
        self.commit=''                # 传输的代码
        self.push=''                  # push命令

        self.init_time=0              # 初始化次数
    
    # -------------------------------------------------------------------------
    def setting(self,git,account,screte,branch_name,your_file):
        '''相关设置'''
        self.git_host=git
        self.account=account
        self.screte=screte
        self.file_path=your_file
        self.branch=branch_name
    
    # -------------------------------------------------------------------------
    def makeTheGitOrder(self):
        '''这里合成用于保存到的git的命令'''
        self.add='git add .'
        commit_msg='update from hasaki quant mechine in time :{}'.format(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        self.commit='git commit -m {}'.format(commit_msg)
        self.push='git push origin {}'.format(self.branch)
    
    def createRepo(self):
        '''创建仓库'''
        pass
    
    def choiceRepo(self):
        '''识别仓库'''
        pass

    def cloneRepo(self):
        '''克隆仓库'''
        pass
    
    def addRepo(self):
        '''对应add命令'''
        index=self.git.index
        index.add(['.'])

    def pushRepo(self):
        '''上传'''
        remote=self.git.remote()
        remote.push()

    # --------------------------------------------------------------------------
    def saveAsGit(self):
        '''外部调用保存成git的命令'''
        if not has_git:
            print('Python环境没有安装GitPython库')
            return 
        else:
            
            if self.init_time==0:
                self.git=Repo(self.file_path)
                self.init_time+=1    

            self.git.git.add('.')
            self.git.git.commit(m='更新')
            self.pushRepo()



class SaveJson:
    def __init__(self):
        self.save_path=''              # json文件的保存地址
        self.json_file=None            # json 数据

    # ---------------------------------------------------------------
    # 数据输入部分
    def dataIn(self,save_path,json_dict):
        '''save_path='xxx/xxx/xxx.json json_dict就是python的字典结构 '''
        self.save_path=save_path
        self.json_file=json_dict
    
    # ---------------------------------------------------------------
    # 保存部
    def save(self):
        '''json持久化'''
        with open(self.save_path,'r',encoding='utf-8') as json_file:
            json.dump(self.json_file,json_file,ensure_ascii=False)